/**
 * 
 */
package name.yzhu.httpserver.method;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.common.base.Joiner;

import name.yzhu.common.ErrorCoder;
import name.yzhu.httpserver.AbstractHttpServlet;
import name.yzhu.httpserver.HttpserverConstant;
import name.yzhu.httpserver.ServerException;

/**
 * @author yhzhu
 *
 */
public class MethodServlet extends AbstractHttpServlet {
	private static final Log LOG = LogFactory.getLog(MethodServlet.class);
	/**
	 * 
	 */
	private static final long serialVersionUID = 717060974358372191L;
	public static final String HRM = "hrm";
	
	private Map<String, Http2Method> contextHRMHandleMap
	   = new HashMap<String, Http2Method>();

	/* (non-Javadoc)
	 * @see name.yzhu.httpserver.AbstractHttpServlet#getContextRoot()
	 */
	@Override
	public String getContextRoot() {
		return "/";
	}

	/* (non-Javadoc)
	 * @see name.yzhu.httpserver.AbstractHttpServlet#getContextPath()
	 */
	@Override
	public String getContextPath() {
		return "/hrm";
	}

	/* (non-Javadoc)
	 * @see name.yzhu.httpserver.AbstractHttpServlet#process(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void process(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String hrm = req.getParameter(HRM);
		if (null == hrm){
			LOG.error("Could not find hrm parameter it is null");
			outputStatus(resp, ErrorCoder.ERRCODE_ILLEGALARGUMENT, 
					errCode.getErrorComment(ErrorCoder.ERRCODE_ILLEGALARGUMENT)+" hrm is null <br><br>"+outputAllHRMHandles());
			return;
		}
		if (hrm.equals("default")){
			resp.getWriter().print(outputAllHRMHandles());
			return;
		}
		Http2Method h2m = contextHRMHandleMap.get(hrm);
		if (null == h2m){
			LOG.error("Could not find method hrm = "+hrm);
			outputStatus(resp, HttpserverConstant.ERRCODE_NOTFOUND_HRM, 
					errCode.getErrorComment(HttpserverConstant.ERRCODE_NOTFOUND_HRM)+" hrm = "+hrm+"<br><br>"+outputAllHRMHandles());
			return;
		}
		
		resp.setContentType(h2m.getMd().getContentType());
		h2m.getHandler().setReqResp(req, resp);
		byte[] retData = invokeMethod(req, resp, h2m);
		if (retData == null){
			outputStatus(resp, ErrorCoder.ERRCODE_BIZ_CLASS_RETURN_NULL, 
					errCode.getErrorComment(ErrorCoder.ERRCODE_BIZ_CLASS_RETURN_NULL));
		}
		else {
			resp.getOutputStream().write(retData);
		}
	}

	private byte[] invokeMethod(HttpServletRequest req, HttpServletResponse resp,
			Http2Method h2m) throws IOException {
		if (h2m.getMd().isAsyncMethod()){
			//TODO: for async
		}
		else {			
			try {
				return h2m.invokeFromHttpReqest(req, resp);
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
				throw new IOException(e);
			}
		}
		return null;
	}

	/**
	 * add biz handle 
	 * @param handle 
	 * @throws ServerException
	 */
	public void addHttpHandle(IHttpHandler handle) throws ServerException{
		Method[] methods = handle.getClass().getMethods();
		for (Method method : methods){
			if (method.isAnnotationPresent(HttpMethodAnno.class)){
				MethodDelcleare md = 
						MethodDelcleare.getInstance(method);
				if (contextHRMHandleMap.containsKey(md.getHrm())){
					String msg = "Duplicated context hrm "+md.getHrm();
					LOG.warn(msg);
					throw new ServerException(msg);
				}
				else{
				    contextHRMHandleMap.put(md.getHrm(), new Http2Method(handle, method, md));
				}
			}
		}
	}
	
	
	public String outputAllHRMHandles(){
		StringBuilder sb = new StringBuilder();
		for (Entry<String, Http2Method> e: contextHRMHandleMap.entrySet()){
			String hrm = e.getKey();
			Http2Method h2m = e.getValue();
			String[] params = h2m.getParams();
			String p = Joiner.on(",").join(params);
			sb.append(hrm);
			sb.append("&");
			sb.append(p);
			sb.append("<br>");
		}
		return sb.toString();
	}
	
}
